package com.pai4j.common.util;

import com.alibaba.nacos.common.utils.MapUtil;
import com.pai4j.common.exception.BusinessException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.util.SafeEncoder;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis 工具类
 *
 * @author: CYM-pai
 * @date: 2025/04/16 16:19
 **/
@Slf4j
@Component
public class RedisUtil {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 发布消息
     *
     * @param channel
     * @param message
     */
    public void sendMessage(String channel, String message) {

        //  redisTemplate.convertAndSend(channel, message);
    }

    /**
     * 执行LUA脚本
     *
     * 该方法用于在Redis中执行LUA脚本，可以接受不同类型的脚本和参数，执行相应的操作
     * 使用泛型T允许该方法返回不同类型的结果
     *
     * @param script Redis脚本对象，包含待执行的LUA脚本
     * @param keys   脚本中使用的键列表，用于操作指定的Redis键
     * @param args   脚本参数，传递给LUA脚本的额外参数
     * @return 执行脚本后的结果，类型由调用者指定
     * @param <T>    泛型参数，表示返回值的类型
     */
    public <T> T executeScript(RedisScript<T> script, List<String> keys, Object... args) {
        try {
            // 使用RedisTemplate执行LUA脚本，传入脚本、键列表和参数
            return redisTemplate.execute(script, keys, args);
        } catch (Exception e) {
            // 捕获执行脚本时可能发生的异常，并记录错误日志
            log.error("redis executeScript error! script:{}, keys:{}, args:{}",
                    script.getScriptAsString(), keys, args, e);
        }
        // 如果执行脚本时发生异常，返回null
        return null;
    }

    /**
     * get取值
     *
     * @param key
     *
     * @return
     */
    public String get(final String key) {
        String result = null;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            result = operations.get(key);
        } catch (Exception e) {
            log.error("redis get error! key:{}", key, e);
        }
        return result;
    }

    /***
     * 批量Get
     * @param keys
     *
     * [1,2,3]
     * [u1,null,u3]
     *
     * @return
     */
    public List<String> mget(final List<String> keys) {
        List<String> result = new ArrayList<>();
        try {
            ValueOperations<String, String> operations = this.redisTemplate.opsForValue();
            return operations.multiGet(keys);
        } catch (Exception e) {
            log.error("redis get error! key:{}", JsonUtil.toJsonString(keys), e);
        }
        return result;
    }

    /**
     * 批量获取并解析Map
     * 该方法通过keys列表从Redis中批量获取值，并将每个键值对存入一个Map中
     * 如果在获取过程中发生异常，将记录错误日志，并返回一个空的或部分填充的Map
     *
     * @param keys 要从Redis中获取的键的列表
     * @return 包含从Redis中获取的键值对的Map，如果发生异常或没有找到键，则可能为空或部分填充
     */
    public Map<String, String> mgetAndParseMap(final List<String> keys) {
        // 初始化一个HashMap用于存储键值对
        Map<String, String> resultMap = new HashMap<>(keys.size());
        try {
            // 批量获取Redis中对应keys的值
            List<String> result = this.mget(keys);
            // 遍历keys和result列表，将键值对存入resultMap
            for (int i = 0; i < keys.size(); i++) {
                resultMap.put(keys.get(i), result.get(i));
            }
            // 返回填充完毕的Map
            return resultMap;
        } catch (Exception e) {
            // 如果发生异常，记录错误日志
            log.error("redis get error! key:{}", JsonUtil.toJsonString(keys), e);
        }
        // 如果发生异常，返回一个空的或部分填充的Map
        return resultMap;
    }

    /**
     * 获取定时任务的锁，有效期为liveTime毫秒
     * 本函数旨在为定时任务提供一种锁机制，以防止多个实例同时执行相同的任务
     * 通过在Redis中设置一个具有过期时间的键值对来实现锁的功能，其中键为任务标识，值为请求ID
     *
     * @param key       定时任务的唯一标识符，用于在Redis中区分不同的任务
     * @param requestId 请求的唯一标识符，用于标识获取锁的请求
     * @param liveTime  锁的有效时间，单位为毫秒，超过该时间后锁将自动释放
     * @return 返回是否成功获取锁，true表示成功获取锁，false表示获取锁失败或参数不合法
     */
    public boolean installLockForMS(String key, String requestId, long liveTime) {
        // 检查key和requestId是否为空，如果任一参数为空，则返回false，表示获取锁失败
        if (org.springframework.util.StringUtils.isEmpty(key) || org.springframework.util.StringUtils.isEmpty(requestId)) {
            return false;
        }
        // 调用setNxPx方法尝试在Redis中设置键值对，以获取锁
        // 如果键不存在，则设置成功，表示获取锁成功；如果键已存在，则设置失败，表示获取锁失败
        return setNxPx(key, requestId, liveTime);
    }

    /**
     * 将给定的值设置为键的值，仅当键不存在时才执行操作，并设置过期时间。
     * 如果键已经存在，则不执行任何操作。
     *
     * @param key 键，用于标识存储的值。
     * @param value 值，要存储的数据。
     * @param exptime 过期时间，单位为毫秒，表示值在多久之后应过期。
     * @return 如果操作成功执行（即键不存在且值被设置），则返回true；否则返回false。
     *
     * 示例:
     * 1 ---> setnx(key, none , 2000) return true;  // 当键不存在时，设置值并设置过期时间为2000毫秒，返回true。
     * 2 ---> setnx return false;  // 当键已经存在时，不执行设置操作，返回false。
     */
    public boolean setNxPx(final String key, final String value, final long exptime) {
        // 使用RedisTemplate执行自定义的Redis命令
        Boolean result = redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            // 获取值序列化器和键序列化器
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();

            // 执行SET命令，参数包括键、值、NX选项（仅在键不存在时设置）、PX选项（设置毫秒级过期时间）和过期时间
            Object obj = connection.execute("set", keySerializer.serialize(key),
                    valueSerializer.serialize(value),
                    SafeEncoder.encode("NX"),
                    SafeEncoder.encode("PX"),
                    Protocol.toByteArray(exptime));
            // 检查命令执行结果，如果结果不为空且为"OK"，则表示操作成功
            return obj != null && "OK".equals(new String((byte[]) obj));
        });
        // 返回操作结果
        return result;
    }

    /**
     * @param key
     * @param value
     * @param exptime
     *
     * @return
     */
    public boolean setNxEx(final String key, final String value, final long exptime) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
            return false;
        }
        Boolean result = redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
            RedisSerializer keySerializer = redisTemplate.getKeySerializer();
            Object obj = connection.execute("set", keySerializer.serialize(key),
                    valueSerializer.serialize(value),
                    SafeEncoder.encode("NX"),
                    SafeEncoder.encode("EX"),
                    Protocol.toByteArray(exptime));
            return ObjectUtils.equals("OK", obj);
        });
        return result;
    }

    /**
     * 带有过期时间的set传值方法。
     * 该方法用于将指定的键值对存储到Redis中，并支持设置过期时间。
     * 如果过期时间大于0，则设置键值对的同时指定过期时间；否则，仅设置键值对而不设置过期时间。
     *
     * @param key        要存储的键，类型为String，不能为空。
     * @param value      要存储的值，类型为String，不能为空。
     * @param expireTime 过期时间，单位为秒。如果大于0，则设置键值对的过期时间；如果小于等于0，则不设置过期时间。
     * @return 操作结果，成功返回true，失败返回false。
     */
    public boolean set(final String key, final String value, long expireTime) {
        boolean result = false;
        try {
            // 获取Redis操作对象，用于执行键值对的存储操作
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            
            // 根据过期时间判断是否需要设置过期时间
            if (expireTime > 0) {
                // 设置键值对并指定过期时间
                operations.set(key, value, expireTime, TimeUnit.SECONDS);
            } else {
                // 仅设置键值对，不指定过期时间
                operations.set(key, value);
            }
            // 操作成功，设置结果为true
            result = true;
        } catch (Exception e) {
            // 捕获异常并记录错误日志，包括键、值和异常信息
            log.error("redis set error! key:{}, value:{}", key, value, e);
        }
        // 返回操作结果
        return result;
    }


    /**
     * set传值
     *
     * @param key
     * @param value
     *
     * @return
     */
    public boolean set(final String key, String value) {
        boolean result = false;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            log.error("redis set error! key:{}, value:{}", key, value);
        }
        return result;
    }
    
    /**
     * 向Redis中的集合（Set）添加一个或多个元素。
     *
     * @param key   集合的键名，用于标识存储在Redis中的集合。
     * @param value 要添加到集合中的一个或多个元素，支持可变参数。
     * @return 如果操作成功返回true，否则返回false。如果发生异常，也会返回false。
     * <p>
     * 该方法通过Spring Data Redis提供的SetOperations接口实现，将指定的元素添加到集合中。
     * 如果添加过程中出现异常，会记录错误日志并返回false。
     */
public boolean sadd(String key, String... value) {
    boolean result = false;
    try {
        // 获取Redis的Set操作对象
        SetOperations<String, String> set = redisTemplate.opsForSet();
        // 执行添加操作
        set.add(key, value);
        result = true;
    } catch (Exception e) {
        // 捕获异常并记录错误日志
        log.error("redis set error! key:{}, value:{}", key, value, e);
    }
    return result;
}


    /**
     * 将一个字符串集合添加到Redis中指定键的Set集合中
     * 如果键不存在，那么创建一个新的Set集合，并添加字符串
     * 如果键存在，那么向现有的Set集合中添加字符串，自动去重
     *
     * @param key Redis中的键
     * @param setValue 要添加到Set集合中的字符串集合
     */
    public void sadd(String key, Set<String> setValue) {
        try {
            // 获取Redis Template的Set操作对象
            SetOperations<String, String> set = redisTemplate.opsForSet();
            // 将字符串集合添加到Redis中指定键的Set集合中
            set.add(key, setValue.toArray(new String[0]));
        } catch (Exception e) {
            // 记录添加操作中的异常错误
            log.error("sadd error! key:{}", key, e);
        }
    }

    /**
     * @param key
     *
     * @return
     */
    public Set<String> smembers(String key) {
        try {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            return set.members(key);
        } catch (Exception e) {
            log.error("redis set error! key:{}", key, e);
            return Collections.emptySet();
        }
    }

    public Long ssize(String key) {
        Long size = null;
        try {
            size = redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error("redis ssize error! key:{}", key, e);
        }
        return size;
    }

    /**
     * @param key
     * @param values
     *
     * @return
     */
    public Long sremove(String key, Object... values) {
        try {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            return set.remove(key, values);
        } catch (Exception e) {
            log.error("redis set error! key:{}", key, e);
            return null;
        }
    }

    /**
     * 元素是否在set集合中
     * @param key
     * @param value
     * @return
     */
    public Boolean sIsMember(String key, String value) {
        try {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            return set.isMember(key, value);
        } catch (Exception ex) {
            log.error("redis sIsMember error! key:{}", key, ex);
            return false;
        }
    }

    /**
     * @param key
     * @param value
     * @param score
     *
     * @return
     */
    public boolean zadd(String key, String value, long score) {
        boolean result = false;
        try {
            ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
            zset.add(key, value, score);
            result = true;
        } catch (Exception e) {
            log.error("redis set error! key:{}, value:{}", key, value, e);
        }
        return result;
    }

    /***
     * 判断zset中是否包含元素
     * @param key
     * @param value
     * @return
     */
    public boolean zContains(String key, String value) {
        try {
            ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
            Long rank = zset.rank(key, value);
            return rank != null && rank >= 0;
        } catch (Exception e) {
            log.error("redis zContains error! key:{}, value:{}", key, value, e);
        }
        return false;
    }

    public Map<String, Boolean> zContains(String key, Set<String> values) {
        if (StringUtils.isBlank(key) || CollectionUtils.isEmpty(values)) {
            return Collections.emptyMap();
        }
        Map<String, Boolean> containMap = new HashMap<>();
        values.forEach(val -> {
            containMap.put(val, this.zContains(key, val));
        });
        return containMap;
    }

    
    /**
     * 向有序集合中添加元素
     *
     * @param key 有序集合的键
     * @param value 要添加的元素值
     * @param score 元素的分数，用于排序
     * @param liveTime 元素的存活时间，单位秒，0表示永不过期
     * @return 添加操作是否成功
     */
    public boolean zadd(String key, String value, long score, long liveTime) {
        boolean result = false;
        try {
            // 获取Redis中操作有序集合的工具
            ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
            // 向有序集合中添加元素
            zset.add(key, value, score);
            // 如果存活时间大于0，则设置键的过期时间
            if (liveTime > 0) {
                expire(key, liveTime);
            }
            // 操作成功，设置结果为true
            result = true;
        } catch (Exception e) {
            // 操作失败，记录错误日志
            log.error("redis set error! key:{}, value:{}", key, value, e);
        }
        // 返回操作结果
        return result;
    }

    /**
     * @param key
     * @param tuples
     * @param liveTime
     *
     * @return
     */
    public boolean zadd(String key, Set<ZSetOperations.TypedTuple<String>> tuples, long liveTime) {
        boolean result = false;
        try {
            ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
            zset.add(key, tuples);
            if (liveTime > 0) {
                expire(key, liveTime);
            }
            result = true;
        } catch (Exception e) {
            log.error("redis set error! key:{}, value:{}", key, tuples, e);
        }
        return result;
    }

    public boolean zIsExist(String key, String value) {
        try {
            ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
            return zset.score(key, value) != null;
        } catch (Exception e) {
            log.error("redis zIsExist error! key:{}, value:{}", key, value, e);
        }
        return false;

    }

    /**
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zrange(String key, long start, long end) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        try {
            Set<String> result = zset.range(key, start, end - 1L);
            return result;
        } catch (Exception e) {
            log.error("redis zrange error,key:{}", key, e);
        }
        return null;
    }

    /***
     * 获取Score
     * @param key
     * @param value
     * @return
     */
    public Long zscore(String key, String value) {
        Double score = redisTemplate.opsForZSet().score(key, value);
        if (score == null) {
            return 0L;
        }
        return score.longValue();
    }

    /**
     * 批量查询zset score评分
     *
     * @param key
     * @param values
     * @return
     */
    public List<Long> zscore(String key, List<String> values) {
        if (StringUtils.isBlank(key) || CollectionUtils.isEmpty(values)) {
            return new ArrayList<>();
        }
        List<Double> scores = redisTemplate.opsForZSet().score(key, values.toArray(new String[values.size()]));
        if (CollectionUtils.isEmpty(scores)) {
            return new ArrayList<>();
        }
        return scores.stream().map(Double::longValue).collect(Collectors.toList());
    }

    /**
     * 批量查询redis zset value对应score评分
     *
     * @param key
     * @param values
     * @return
     */
    public Map<String, Long> zscoreTomap(String key, List<String> values) {
        List<Long> scores = this.zscore(key, values);
        if (CollectionUtils.isEmpty(scores)) {
            return new HashMap<>();
        }
        Map<String, Long> scoreMap = new HashMap<>();
        for (int i = 0; i < values.size(); i++) {
            scoreMap.put(values.get(i), scores.get(i));
        }
        return scoreMap;
    }

    /**
     * 根据指定的键和索引范围，从Redis的有序集中获取元素，并以从大到小的顺序返回
     * 索引范围是从0开始计数的，例如start为0，end为2，则表示获取最大的三个元素
     * 如果end为-1，则表示获取所有元素
     *
     * @param key Redis中有序集的键
     * @param start 获取范围的起始索引（包含）
     * @param end 获取范围的结束索引（不包含）
     *
     * @return 一个包含指定范围内元素的集合，元素按从大到小的顺序排列
     */
    public Set<String> zreverseRange(String key, long start, long end) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        try {
            // 从Redis中按指定范围获取有序集元素，注意结束索引是exclusive（不包含）
            Set<String> result = zset.reverseRange(key, start, end - 1L);
            return result;
        } catch (Exception e) {
            // 记录异常信息，便于问题排查
            log.error("redis zrange error,errorMessage:{}", e.getMessage());
        }
        // 如果发生异常，返回一个空的集合
        return new HashSet<>();
    }

    /**
     * 获取ZSet分数区间中前几位的元素
     * @param key
     * @param maxScore
     * @param offset
     * @param count
     * @return
     */
    public Set<String> zreverseRangeByScore(String key, double maxScore, long offset, long count) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        try {
            Set<String> result = zset.reverseRangeByScore(key, 1 - Double.MAX_VALUE, maxScore, offset, count);
            return result;
        } catch (Exception e) {
            log.error("redis zrangeByScoreWithScores error,errorMessage:{}",
                    e.getMessage());
        }
        return null;
    }

    /**
     * 获取ZSet分数区间中前几位的元素
     * @param key
     * @param minScore
     * @param maxScore
     * @param offset
     * @param count
     * @return
     */
    public Set<String> zreverseRangeByScore(String key, double minScore, double maxScore, long offset, long count) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        try {
            Set<String> result = zset.reverseRangeByScore(key, minScore, maxScore, offset, count);
            return result;
        } catch (Exception e) {
            log.error("redis zrangeByScoreWithScores error,errorMessage:{}",
                    e.getMessage());
        }
        return null;
    }

    /**
     * 获取ZSet中所有元素总数
     * @param key
     */
    public long zSetSize(String key) {
        if (StringUtils.isEmpty(key)) {
            return 0;
        }
        try {
            return redisTemplate.opsForZSet().size(key);
        } catch (Exception ex) {
            log.error("zSetSize error", ex);
            return 0;
        }
    }

    /**
     * 获取ZSet中所有分数区间元素总数
     * @param key
     */
    public long zSetCount(String key, double minScore, double maxScore) {
        if (StringUtils.isEmpty(key)) {
            return 0;
        }
        try {
            return redisTemplate.opsForZSet().count(key, minScore, maxScore);
        } catch (Exception ex) {
            log.error("zSetCount error", ex);
            return 0;
        }
    }

    /**
     * 获取ZSet中所有元素
     * @param key
     */
    public Set<String> zSetGetAll(String key) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        try {
            return redisTemplate.opsForZSet().reverseRange(key, 0, -1);
        } catch (Exception ex) {
            log.error("zSetGetAll error", ex);
            return null;
        }
    }


    /**
     * 根据分数范围获取有序集合中的元素
     *
     * @param key Redis中有序集合的键
     * @param start 分数的起始范围（包含）
     * @param end 分数的结束范围（包含）
     *
     * @return 返回在分数范围内的有序集合元素，如果出现异常则返回null
     */
    public Set<String> zrangeByScore(String key, long start, long end) {
        // 获取RedisTemplate的ZSetOperations对象，用于操作有序集合
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        
        try {
            // 使用rangeByScore方法获取指定分数范围内的元素
            Set<String> result = zset.rangeByScore(key, start, end);
            // 返回查询结果
            return result;
        } catch (Exception e) {
            // 记录异常日志，方便问题排查
            log.error("redis zrangeByScore error,errorMessage:{}", e.getMessage());
        }
        // 如果发生异常，返回null
        return null;
    }
    /**
     * 按照分数范围删除
     *
     * @param key
     * @param start
     * @param end
     */
    public void zremoveRangeByScore(String key, long start, long end) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        try {
            zset.removeRangeByScore(key, start, end);
        } catch (Exception e) {
            log.error("redis zremoveRangeByScore error,errorMessage:{}", e.getMessage());
        }
    }

    /**
     * 删除ZSet中某一元素
     * @param key
     * @param value
     */
    public void zremove(String key, String value) {
        if (StringUtils.isEmpty(key)) {
            return;
        }
        try {
            redisTemplate.opsForZSet().remove(key, value);
        } catch (Exception ex) {
            log.error("zSetAdd error", ex);
        }
    }

    /**
     * Zset取交集
     * {key} 与 {otherKey} 交集赋值给 {destKey}
     */
    public void zintersectAndStore(String key, String otherKey, String destKey) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(otherKey) || StringUtils.isEmpty(destKey)) {
            return;
        }
        try {
            redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey);
        } catch (Exception ex) {
            log.error("zSetAdd error", ex);
        }
    }

    /**
     * Zset取交集(使用lua的方式)
     * {key} 与 {otherKey} 交集赋值给 {destKey}，分数取最小的值
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zinterstoreMin(String key, String otherKey, String destKey) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(otherKey) || StringUtils.isEmpty(destKey)) {
            return null;
        }
        List<String> keys = Arrays.asList(destKey, key, otherKey);
        String script = " return redis.call('zinterstore', KEYS[1], '2', KEYS[2], KEYS[3], 'aggregate', 'min') ";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        try {
            Object res = redisTemplate.execute(redisScript, keys);
            return (Long) res;
        } catch (Exception ex) {
            log.error("zinterstoreMin error, key:{}, otherKey:{}, destKey:{}", key, otherKey, destKey, ex);
        }
        return null;
    }

    /**
     * zset复制数据
     * 将{key}的数据复制到{destKey}上
     * @param key
     * @param destKey
     * @return
     */
    public Long zunionstore(String key, String destKey) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(destKey)) {
            return null;
        }
        List<String> keys = Arrays.asList(destKey, key);
        String script = " return redis.call('zunionstore', KEYS[1], '1', KEYS[2]) ";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        try {
            Object res = redisTemplate.execute(redisScript, keys);
            return (Long) res;
        } catch (Exception ex) {
            log.error("zunionstore error, key:{}, destKey:{}", key, destKey, ex);
        }
        return null;
    }


    /**
     * @param key
     *
     * @return
     */
    public Long zsize(String key) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        try {
            Long result = zset.size(key);
            return result;
        } catch (Exception e) {
            log.error("redis zrange error,errorMessage:{}", e.getMessage());
        }
        return null;
    }

    /**
     * hset
     *
     * 在Redis中为指定的哈希表设置字段的值。
     * 如果给定字段已经存在，那么更新这个字段的值。
     * 如果哈希表不存在，会创建一个空的哈希表，并设置字段的值。
     * 此外，可以通过可选参数expire来设置哈希表的过期时间。
     *
     * @param key 哈希表的键，不能为空。
     * @param field 哈希表中的字段，不能为空。
     * @param value 要设置的字段的值，不能为空。
     * @param expire 可选参数，用于设置哈希表的过期时间，以秒为单位。如果传递了且大于0，将设置过期时间。
     * @return 如果字段是新的且设置成功返回true，否则返回false。如果设置了过期时间，将在成功设置字段后应用。
     */
    public boolean hset(String key, String field, String value, long... expire) {
        // 检查关键参数是否为空，为空则直接返回false，表示操作失败。
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(field) || StringUtils.isEmpty(value)) {
            return false;
        }
        boolean result = false;
        try {
            // 序列化键、字段和值，以备在Redis操作中使用。
            final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
            final byte[] rawField = redisTemplate.getStringSerializer().serialize(field);
            final byte[] rawValue = redisTemplate.getStringSerializer().serialize(value);
            
            // 执行Redis操作，包括设置哈希表字段的值和可选的过期时间。
            result = redisTemplate.execute(connection -> {
                boolean ret = connection.hSet(rawKey, rawField, rawValue);
                // 如果设置了过期时间且大于0，则应用过期时间。
                if (expire.length > 0 && expire[0] > 0) {
                    connection.expire(rawKey, expire[0]);
                }
                return ret;
            }, true);
        } catch (Exception ex) {
            // 如果操作过程中发生异常，记录错误日志。
            log.error("hset error", ex);
        }
        // 返回操作结果。
        return result;
    }

    /**
     * 从Redis中获取哈希表中给定字段的值
     *
     * @param key 哈希表的键，用于指定存储数据的哈希表，不能为空或空字符串
     * @param field 哈希表中的字段，用于指定要获取的值的字段，不能为空或空字符串
     *
     * @return 返回给定字段的值如果字段不存在或发生错误，则返回null
     */
    public String hget(String key, String field) {
        // 检查key和field是否为空或空字符串，因为Redis操作需要有效的键和字段
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(field)) {
            return null;
        }
        // 序列化key和field，以便在Redis中进行操作，Redis操作需要以字节形式进行
        final byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
        final byte[] rawField = redisTemplate.getStringSerializer().serialize(field);
        // 执行hGet操作，从Redis中获取哈希表中指定字段的值
        final byte[] rawValue = redisTemplate.execute(connection -> connection.hGet(rawKey, rawField), true);
        // 反序列化获取到的字节数组，转换为字符串返回
        return redisTemplate.getStringSerializer().deserialize(rawValue);
    }
    /**
     * @param key
     * @param field
     *
     * @return
     */
    public Long hdel(String key, String field) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(field)) {
            return null;
        }
        long result = redisTemplate.opsForHash().delete(key, field);
        return result;
    }

    /**
     * 从Redis中删除指定哈希表中的多个字段
     *
     * @param key 哈希表的键，不能为null或空字符串
     * @param fields 要删除的字段列表，不能为null或空列表
     * @return 删除的字段数量如果key或fields为空，则返回null
     */
    public Long hmdel(String key, List<String> fields) {
        // 检查key和fields是否为空，如果任一条件满足，则返回null
        if (StringUtils.isEmpty(key) || CollectionUtils.isEmpty(fields)) {
            return null;
        }
        // 过滤掉fields列表中的null值，并将结果转换为Object数组
        Object[] filteredFields = fields.stream()
                .filter(Objects::nonNull)
                .toArray();
        // 使用redisTemplate的opsForHash()方法删除指定哈希表中的多个字段，并返回删除的字段数量
        long result = redisTemplate.opsForHash().delete(key, filteredFields);
        // 返回删除的字段数量
        return result;
    }

    /**
     * @param key
     * @param queryFields
     *
     * @return
     */
    public List<String> hmget(String key, List<String> queryFields) {
        HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
        return hashOperations.multiGet(key, queryFields);
    }

    /**
     * @param key
     * @param map
     *
     * @return
     */
    public void hmSet(String key, Map<Long, Long> map) {
        if (StringUtils.isEmpty(key) || MapUtil.isEmpty(map)) {
            return;
        }
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * @param key
     * @param map
     */
    public void hmSet(String key, Map<String, String> map, long... expire) {
        if (StringUtils.isEmpty(key) || MapUtil.isEmpty(map)) {
            return;
        }
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 在Redis中为指定的hash键中的字段增加值
     * 如果key或field为空，则返回null
     * 如果执行成功，返回增加后的值；如果失败或key/field不存在，则返回null
     *
     * @param key Redis中的hash键
     * @param field hash键中的字段
     * @return 增加后的字段值，如果操作失败或参数为空则返回null
     */
    public Long hincrex(String key, String field) {
        // 检查key和field是否为空，如果任一为空，则返回null
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(field)) {
            return null;
        }
        // 使用redisTemplate的hash操作，为指定hash键中的字段增加1
        Long result = redisTemplate.opsForHash().increment(key, field, 1);
        // 返回增加后的字段值
        return result;
    }
    /**
     * 在Redis的哈希表中，对指定字段的数值进行增量操作
     * 如果key或field为空，则返回null，表示不执行操作
     * 此方法解释了为什么需要检查key和field是否为空：为了避免无效操作和潜在的错误
     *
     * @param key 不能为空，表示哈希表的名称
     * @param field 不能为空，表示哈希表中要增量的字段
     * @param val 要增量的值，可以是任何长整型数值
     * @return 返回增量后的值，如果操作因参数无效而未执行，则返回null
     */
    public Long hIncrementVal(String key, String field, long val) {
        // 检查key和field是否为空，如果任一参数为空，则返回null，表示不执行操作
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(field)) {
            return null;
        }
        // 执行哈希表中的字段增量操作，并返回增量后的值
        Long result = redisTemplate.opsForHash().increment(key, field, val);
        // 返回增量后的值
        return result;
    }

    /**
     * 对指定key的hash结构中的field字段进行递减操作
     * @param key hash结构的key
     * @param field hash中的字段名
     * @return 递减后的字段值，如果key或field为空则返回null
     */
    public Long hdecrex(String key, String field) {
        // 参数校验，key或field为空时直接返回null
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(field)) {
            return null;
        }
        // 使用redis的hash操作对指定字段进行-1递减操作
        Long result = redisTemplate.opsForHash().increment(key, field, -1);
        return result;
    }


    public void lleftPush(String key, String value) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
            return;
        }
        redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 将多个元素添加到列表的左侧
     * 如果键为空或值集合为空，则不执行任何操作
     * 此方法使用Redis的左推入（left push）操作，将集合中的所有元素一次性添加到列表的左侧
     *
     * @param key 列表的键
     * @param values 要添加到列表的元素集合
     */
    public void lleftPushAll(String key, Collection values) {
        // 检查键和值集合是否为空，如果任一条件满足，则直接返回，不执行任何操作
        if (StringUtils.isEmpty(key) || CollectionUtils.isEmpty(values)) {
            return;
        }
        // 使用Redis模板的左推入操作，将所有值一次性添加到列表的左侧
        redisTemplate.opsForList().leftPushAll(key, values);
    }

    public void lrightPush(String key, String value) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
            return;
        }
        redisTemplate.opsForList().rightPush(key, value);
    }

    public void lrightPushAll(String key, List<String> valueList) {
        if (StringUtils.isEmpty(key) || CollectionUtils.isEmpty(valueList)) {
            return;
        }
        redisTemplate.opsForList().rightPushAll(key, valueList);
    }

    public void lleftPushAll(String key, List<String> valueList) {
        if (StringUtils.isEmpty(key) || CollectionUtils.isEmpty(valueList)) {
            return;
        }
        redisTemplate.opsForList().leftPushAll(key, valueList);
    }

    public Long lsize(String key) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        return redisTemplate.opsForList().size(key);
    }

    /**
     * @param key
     * @param start
     * @param end
     */
    public List<String> lrange(String key, long start, long end) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 对列表进行修剪，仅保留指定区间内的元素
     * 等价于 Redis LTRIM key start end（end 为包含区间）
     *
     * @param key   列表键
     * @param start 起始下标（包含）
     * @param end   结束下标（包含）
     */
    public void ltrim(String key, long start, long end) {
        if (StringUtils.isEmpty(key)) {
            return;
        }
        try {
            redisTemplate.opsForList().trim(key, start, end);
        } catch (Exception e) {
            log.error("redis ltrim error! key:{}, start:{}, end:{}", key, start, end, e);
        }
    }

    /**
     * @param key
     *
     * @return
     */
    public String lleftPop(String key) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 从指定列表的右侧（尾部）弹出一个元素
     *
     * @param key 列表的键，不能为空
     * @return 列表尾部的元素如果列表为空或键不存在，则返回null
     */
    public String lrightPop(String key) {
        // 检查传入的键是否为空，为空则返回null
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        // 使用Redis模板从指定列表的右侧弹出一个元素
        return redisTemplate.opsForList().rightPop(key);
    }
    /**
     * @param key
     * @param time
     * @param timeUnit
     * @return
     */
    public String lleftPop(String key, long time, TimeUnit timeUnit) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        try {
            return redisTemplate.opsForList().leftPop(key, time, timeUnit);
        } catch (Exception e) {
            log.error("redis lleftPop error! key:{}", key, e);
        }
        return null;
    }

    /**
     * @param key
     * @param time
     * @param timeUnit
     * @return
     */
    public String lrightPop(String key, long time, TimeUnit timeUnit) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        try {
            return redisTemplate.opsForList().rightPop(key, time, timeUnit);
        } catch (Exception e) {
            log.error("redis lrightPop error! key:{}", key, e);
        }
        return null;
    }

    /**
     * 带失效时间的值原子自增
     *
     * @param key 键名
     * @param sec 失效时间（秒）
     *
     * @return 自增后的值
     */
    public Long increx(String key, long sec) {
        Long result = null;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            // 原子自增操作
            result = operations.increment(key, 1);
            // 第一次设置，设置有效时间
            if (result == 1) {
                expire(key, sec);
            }
        } catch (Exception e) {
            // 记录异常日志
            log.error("redis get error! key:{}", key, e);
        }
        // 返回自增后的值
        return result;
    }

    /**
     * 带失效时间的值原子自减
     *
     * @param key
     * @param sec
     *
     * @return
     */
    public Long decrex(String key, long sec) {
        Long result = null;
        try {
            ValueOperations<String, String> operations = redisTemplate.opsForValue();
            result = operations.increment(key, -1);
            // 第一次设置，设置有效时间
            if (result == -1) {
                expire(key, sec);
            }
        } catch (Exception e) {
            log.error("redis get error! key:{}", key, e);
        }
        return result;
    }

    /**
     * 获取定时任务的锁（非集群方式）
     *
     * @param key       键值
     * @param requestId 锁ID，通过此来判断是哪个实例的锁
     * @param liveTime  过期时间
     *
     * @return
     */
    public boolean installDistributedLock(String key, String requestId, long liveTime) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(requestId)) {
            return false;
        }
        return setNxEx(key, requestId, liveTime);
    }

    /**
     * 释放定时任务的锁
     *
     * @param key       键值
     * @param requestId 锁ID
     */
    public void releaseDistributedLock(String key, String requestId) {
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(requestId)) {
            return;
        }
        try {
            String redisId = redisTemplate.opsForValue().get(key);
            // 保证只解自己拿到的锁
            if (requestId.equals(redisId)) {
                redisTemplate.delete(key);
            }
        } catch (Exception e) {
            log.error("release distributed lock error,requestId:{},errorMessage:{}",
                    requestId, e.getMessage());
            throw new BusinessException("release distributed lock error");
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public void remove(final String key) {
        redisTemplate.delete(key);
    }

    public void removeAll(final Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 根据模式查找键
     *
     * @param pattern 键的模式，支持通配符 * 和 ?
     * @return 匹配的键集合
     */
    public Set<String> keys(String pattern) {
        try {
            return redisTemplate.keys(pattern);
        } catch (Exception e) {
            log.error("redis keys error! pattern:{}", pattern, e);
            return new HashSet<>();
        }
    }

    /**
     * 获取键的剩余生存时间
     *
     * @param key 键名
     * @return 剩余生存时间（秒），-1表示永不过期，-2表示键不存在
     */
    public Long ttl(String key) {
        try {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("redis ttl error! key:{}", key, e);
            return -2L;
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     *
     * @return
     */
    public Boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param livetime
     */
    public void expire(String key, long livetime) {
        redisTemplate.expire(key, livetime, TimeUnit.SECONDS);
    }

    /***
     * 求两个set的交集
     */
    public Set<String> intersect(String key1, String key2) {
        try {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            return set.intersect(key1, key2);
        } catch (Exception e) {
            log.error("redis intersect error! key1:{}, key2:{}", key1, key2, e);
            return Collections.emptySet();
        }
    }

    /***
     * 求两个set的并集
     */
    public Set<String> union(String key1, String key2) {
        try {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            return set.union(key1, key2);
        } catch (Exception e) {
            log.error("redis union error! key1:{}, key2:{}", key1, key2, e);
            return Collections.emptySet();
        }
    }

    public Long size(String key) {
        SetOperations<String, String> set = redisTemplate.opsForSet();
        try {
            Long result = set.size(key);
            return result;
        } catch (Exception e) {
            log.error("redis size error, key:{}, errorMessage:{}",key, e.getMessage());
        }
        return null;
    }

    /***
     * 查询剩余存活时间
     * @param key
     * @return
     */
    public Long getKeysExpireTime(String key) {
        Set<String> keys = redisTemplate.keys(key);
        return redisTemplate.opsForValue().getOperations().getExpire(key);
    }

    /***
     * 根据前缀查询所有key的剩余存活时间
     * @return
     */
    public Map<String, Long> batchGetKeysExpireTime(String keyPrefix) {
        Set<String> keys = redisTemplate.keys(keyPrefix + "*");
        Map<String, Long> keyExpireMap = new HashMap<>();
        for (String key : keys) {
            Long expireTime = redisTemplate.opsForValue().getOperations().getExpire(key);
            keyExpireMap.put(key, expireTime);
        }
        return keyExpireMap;
    }

    /**
     * @param start
     * @param end
     * @return
     */
    public Map<Long, Set<DefaultTypedTuple>> zreverseRangeWithScoreByPipelined(List<String> keys,
                                                                               List<Long> indexs,
                                                                               long start, long end) {
        if (CollectionUtils.isEmpty(keys)) {
            return new HashMap<>();
        }
        List<Object> redisResult = redisTemplate.executePipelined((RedisCallback<List<Object>>) connection ->  {
            for (String key : keys) {
                byte[] rewKey = redisTemplate.getStringSerializer().serialize(key);
                connection.zRevRangeWithScores(rewKey, start, end - 1);
            }
            return null;
        });
        Map<Long, Set<DefaultTypedTuple>> resultMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(redisResult)) {
            for (int i = 0; i < indexs.size(); i++) {
                Set<DefaultTypedTuple> result = (Set<DefaultTypedTuple>) redisResult.get(i);
                resultMap.put(indexs.get(i), result);
            }
        }
        return resultMap;
    }
}